package com.funny.stock.util;

import org.springframework.security.crypto.codec.Base64;
import org.springframework.security.crypto.codec.Hex;
import org.springframework.security.crypto.codec.Utf8;
import org.springframework.security.crypto.password.PasswordEncoder;
import org.springframework.util.Assert;

import java.security.MessageDigest;
import java.security.NoSuchAlgorithmException;

/*
 * @author  pengl
 * @date    2020/4/9
 */
public class CustomPasswordEncoder implements PasswordEncoder {
    private final String algorithm;
    private int iterations;

    public CustomPasswordEncoder(String algorithm) {
        this(algorithm, false);
    }

    public CustomPasswordEncoder(String algorithm, boolean encodeHashAsBase64) throws IllegalArgumentException {
        this.iterations = 1;
        this.algorithm = algorithm;
        this.setEncodeHashAsBase64(encodeHashAsBase64);
        this.getMessageDigest();
    }

    public String encodePassword(String rawPass, Object salt) {
        String saltedPass = this.mergePasswordAndSalt(rawPass, salt, false);
        MessageDigest messageDigest = this.getMessageDigest();
        byte[] digest = messageDigest.digest(Utf8.encode(saltedPass));

        for(int i = 1; i < this.iterations; ++i) {
            digest = messageDigest.digest(digest);
        }

        return this.getEncodeHashAsBase64() ? Utf8.decode(Base64.encode(digest)) : new String(Hex.encode(digest));
    }

    protected final MessageDigest getMessageDigest() throws IllegalArgumentException {
        try {
            return MessageDigest.getInstance(this.algorithm);
        } catch (NoSuchAlgorithmException var2) {
            throw new IllegalArgumentException("No such algorithm [" + this.algorithm + "]");
        }
    }

    private boolean isPasswordValid(String encPass, String rawPass, Object salt) {
        String pass1 = "" + encPass;
        String pass2 = this.encodePassword(rawPass, salt);
        return PasswordEncoderUtils.equals(pass1, pass2);
    }

    public String getAlgorithm() {
        return this.algorithm;
    }

    public void setIterations(int iterations) {
        Assert.isTrue(iterations > 0, "Iterations value must be greater than zero");
        this.iterations = iterations;
    }

    protected String[] demergePasswordAndSalt(String mergedPasswordSalt) {
        if (mergedPasswordSalt != null && !"".equals(mergedPasswordSalt)) {
            String password = mergedPasswordSalt;
            String salt = "";
            int saltBegins = mergedPasswordSalt.lastIndexOf("{");
            if (saltBegins != -1 && saltBegins + 1 < mergedPasswordSalt.length()) {
                salt = mergedPasswordSalt.substring(saltBegins + 1, mergedPasswordSalt.length() - 1);
                password = mergedPasswordSalt.substring(0, saltBegins);
            }

            return new String[]{password, salt};
        } else {
            throw new IllegalArgumentException("Cannot pass a null or empty String");
        }
    }

    protected String mergePasswordAndSalt(String password, Object salt, boolean strict) {
        if (password == null) {
            password = "";
        }

        if (strict && salt != null && (salt.toString().lastIndexOf("{") != -1 || salt.toString().lastIndexOf("}") != -1)) {
            throw new IllegalArgumentException("Cannot use { or } in salt.toString()");
        } else {
            return salt != null && !"".equals(salt) ? password + "{" + salt.toString() + "}" : password;
        }
    }

    private boolean encodeHashAsBase64 = false;


    public boolean getEncodeHashAsBase64() {
        return this.encodeHashAsBase64;
    }

    public void setEncodeHashAsBase64(boolean encodeHashAsBase64) {
        this.encodeHashAsBase64 = encodeHashAsBase64;
    }

    @Override
    public String encode(CharSequence rawPassword) {
        return encodePassword(rawPassword.toString(),"");
    }

    @Override
    public boolean matches(CharSequence rawPassword, String encodedPassword) {
        return isPasswordValid(encodedPassword, rawPassword.toString(),"");
    }
}
